<template>
    <div
        class="page_root"
        :key="queryUpdateFlag"
        data-page-name="平台目标-版本任务弹窗"
        data-page-identification="j_r4p6uatw7v"
    >
        <a-spin :spinning="pageLoading.loading" :tip="pageLoading.tip">
            <!-- begin vid_74h0kaeyby 自定义模板_ot1oz7qqmz -->
            <div class="body">
                <div class="table-box-story">
                    <div class="expend-btn" @click="toggleExpandAll">
                        <a-spin :spinning="expendSping">
                            <PlusSquareOutlined v-if="!isAllExpanded" title="展开全部" />
                            <MinusSquareOutlined v-if="isAllExpanded" title="收起全部" />
                        </a-spin>
                    </div>
                    <a-table
                        :columns="columns"
                        :data-source="state.dataSource"
                        bordered
                        :loading="state.tableLoading"
                        :scroll="{ x: '1200px', y: state.tableHeight }"
                        :pagination="false"
                        row-key="id"
                        size="small"
                        :rowSelection="storyRowSelection"
                        @resizeColumn="handleResizeStoryColumn"
                        :expandedRowKeys="state.expandedRowKeys"
                        @expandedRowsChange="keys => (state.expandedRowKeys = keys)"
                    >
                        <template #bodyCell="{ column, record }">
                            <template v-if="column.dataIndex === 'storyName'">
                                <div class="task-name-body">
                                    <div class="task-title">
                                        <div title="用户故事" class="type-tag story-bg" style="color: #3875ff"></div>
                                        <div
                                            v-if="record.keynote === '是'"
                                            class="critical-path"
                                            title="关键路径"
                                        ></div>

                                        <!-- 需求编号：只在存在时显示；用 wrapper 保证 tooltip 有单一根元素 -->
                                        <template v-if="hasNumberList(record)">
                                            <a-tooltip :title="formatAllCodes(record.numberList)" placement="top">
                                                <span class="tags-wrapper">
                                                    <a-tag color="purple" class="mr-1">{{
                                                        formatFirstCode(record.numberList)
                                                    }}</a-tag>
                                                </span>
                                            </a-tooltip>
                                        </template>

                                        <!-- 标签展示：只有在有标签数组时显示；tooltip 内部使用单一 wrapper 元素 -->
                                        <template v-if="hasLabels(record)">
                                            <a-tooltip :title="formatAllLabels(record.labels)" placement="top">
                                                <span class="tags-wrapper">
                                                    <!-- v-for 放在 a-tag 上，key 使用 label+index 保证稳定 -->
                                                    <a-tag
                                                        v-for="(label, idx) in formatShowLabels(record.labels)"
                                                        :key="`${label}::${idx}`"
                                                        color="blue"
                                                        class="mr-1"
                                                    >
                                                        {{ label }}
                                                    </a-tag>
                                                </span>
                                            </a-tooltip>
                                        </template>

                                        <div
                                            class="story-name-color"
                                            :title="record.storyName"
                                            @click="showDescription(record, '故事')"
                                        >
                                            {{ record.storyName }}
                                        </div>
                                    </div>
                                </div>
                            </template>

                            <template v-if="column.dataIndex === 'operation'">
                                <a-button
                                    type="link"
                                    size="small"
                                    @click="jumpTask(props.type, record.versionId, record.projectId, record.id)"
                                    >查看</a-button
                                >
                            </template>
                            <template v-if="column.dataIndex === 'storyStatus'">
                                <div
                                    title="用户故事状态"
                                    class="status-tag story-status"
                                    :style="{
                                        backgroundColor: getStatusColor(record.storyStatus)
                                    }"
                                    >{{ record.storyStatus }}</div
                                >
                            </template>
                            <template v-if="column.dataIndex === 'progress'">
                                <a-tooltip>
                                    <template #title>
                                        <div>
                                            <div>任务总数{{ record.taskNumber }}个</div>
                                            <div class="flex-color">
                                                <div class="no-start"></div>
                                                <div>未开始任务数{{ record.noStartTaskNumber }}个</div>
                                            </div>
                                            <div class="flex-color">
                                                <div class="running"></div>
                                                <div>进行中任务数{{ record.runningTaskNumber }}个</div>
                                            </div>
                                            <div class="flex-color">
                                                <div class="finish"></div>
                                                <div>已完成任务数{{ record.finishTaskNumber }}个</div>
                                            </div>
                                        </div>
                                    </template>
                                    <div style="display: flex; gap: 8px">
                                        <a-progress
                                            style="width: 150px"
                                            :percent="totalPercent(record)"
                                            :success="{ percent: finishPercent(record), strokeColor: '52c41a' }"
                                            :show-info="false"
                                            trailColor="#d9d9d9"
                                            strokeColor="#3875ff"
                                        />
                                        <div
                                            >{{
                                                formatPercentage(
                                                    record.taskNumber
                                                        ? (record.finishTaskNumber / record.taskNumber) * 100
                                                        : 0
                                                )
                                            }}%</div
                                        >
                                    </div>
                                </a-tooltip>
                            </template>
                            <template v-if="column.dataIndex === 'priority'">
                                <div
                                    class="priority-tag"
                                    title="优先级"
                                    :style="{ backgroundColor: getBgc(record.priority) }"
                                    >{{ record.priority || 'NO' }}</div
                                >
                            </template>
                        </template>
                        <template #expandedRowRender="{ record }">
                            <a-table
                                :columns="innerColumns"
                                :data-source="record.taskChildrenList"
                                :pagination="false"
                                bordered
                                class="table-box-task"
                                :scroll="{ x: '1800px' }"
                                row-key="taskId"
                                :rowSelection="
                                    state.showCheck
                                        ? {
                                              selectedRowKeys: state.selectedTaskKeys[record.id] || [],
                                              onChange: keys => {
                                                  state.selectedTaskKeys[record.id] = keys
                                              },
                                              getCheckboxProps: taskRecord => ({
                                                  disabled: taskRecord.taskType === '问题'
                                              })
                                          }
                                        : null
                                "
                                @resizeColumn="handleResizeTaskColumn"
                            >
                                <template #headerCell="{ column, title }">
                                    <template v-if="title === '操作'">
                                        <div style="display: flex; align-items: center; justify-content: center">{{
                                            title
                                        }}</div>
                                    </template>
                                </template>
                                <template #bodyCell="{ column, record }">
                                    <template v-if="column.dataIndex === 'priority'">
                                        <div
                                            class="priority-tag"
                                            title="优先级"
                                            :style="{ backgroundColor: getBgc(record.priority) }"
                                            >{{ record.priority || 'NO' }}</div
                                        >
                                    </template>
                                    <!--                                      任务名称-->
                                    <template v-if="column.dataIndex === 'taskName'">
                                        <div class="task-name-body">
                                            <div class="task-title">
                                                <div
                                                    :title="record.taskType"
                                                    class="type-tag"
                                                    :class="[classMap[record.taskType] || '']"
                                                >
                                                </div>
                                                <div
                                                    v-if="record.keynote === '是'"
                                                    class="critical-path"
                                                    title="关键路径"
                                                ></div>
                                                <div class="name-color" :title="record.taskName">{{
                                                    record.taskName
                                                }}</div>
                                            </div>
                                        </div>
                                    </template>
                                    <!--                                      任务状态-->
                                    <template v-if="column.dataIndex === 'taskStatus'">
                                        <div class="edit-approve">
                                            <div
                                                class="status-tag"
                                                title="任务状态"
                                                :style="{
                                                    backgroundColor: getStatusColor(record.taskStatus)
                                                }"
                                                >{{ record.taskStatus || '暂无' }}</div
                                            >
                                            <div
                                                title="审批详情-变更"
                                                style="padding: 3px; border-left: 1px solid #fff; background: #fff"
                                                @click.stop="
                                                    getTaskStatus(
                                                        record.taskId,
                                                        0,
                                                        record.milepostId,
                                                        record.taskStatus,
                                                        record.taskType,
                                                        record
                                                    )
                                                "
                                            >
                                                <div class="can-edit"></div>
                                            </div>
                                        </div>
                                    </template>
                                    <!--                                      附件模板-->
                                    <template v-if="column.dataIndex === 'attachmentTemplateList'">
                                        <div
                                            v-if="
                                                record.attachmentTemplateList &&
                                                record.attachmentTemplateList.length > 0
                                            "
                                            class="item-content main-font under-line"
                                            @click="showMore(record.attachmentTemplateList)"
                                        >
                                            查看
                                        </div>
                                    </template>
                                    <!--                                      附件-->
                                    <template v-if="column.dataIndex === 'attachmentList'">
                                        <div
                                            v-if="record.attachmentList?.length > 0"
                                            class="item-content main-font under-line"
                                            @click="showMore(record.attachmentList)"
                                            >查看</div
                                        >
                                    </template>
                                </template>
                            </a-table>
                        </template>
                    </a-table>
                </div>
            </div>
            <!-- end vid_74h0kaeyby 自定义模板_ot1oz7qqmz -->
        </a-spin>
    </div>
</template>
<script setup lang="tsx">
    import {
        h,
        ref,
        reactive,
        toRaw,
        markRaw,
        watch,
        computed,
        onBeforeMount,
        onMounted,
        onBeforeUnmount,
        onUnmounted,
        onActivated,
        onDeactivated
    } from 'vue'
    import { useRoute, useRouter } from 'vue-router'
    const router = useRouter()
    const route = useRoute()
    import { get, post, put, del, request, requestWithHeaders } from '@/utils/http/Axios'
    import {
        cloneDeep,
        localGet,
        merge,
        getDifferenceArray,
        traversalTreeStructure,
        checkCaseCondition,
        renderStringInTemplate,
        handleBreadcrumbJump,
        throttle,
        permission,
        getComponentParamsValue
    } from '@/utils/index'
    import { isNullOrEmpty, isEmptyArr, isNullOrUnDef, isContained, isArray, isEmpty, isObject } from '@/utils/is'
    import dayjs from 'dayjs'
    import { getPagePcChartsDataSourceConvert } from '@/utils/chart'
    import { message, notification, Modal } from 'ant-design-vue'
    import {
        propertyFormatByNumber,
        propertyFormatByPercentage,
        propertyFormatByDate,
        parseSubFromDataList
    } from '@/utils/format'
    import colorTheme from '@/design/custom-theme.json'
    import { QuillEditor } from '@vueup/vue-quill'
    /*** PUBLISH_EXEGESIS ***
 import UserTableRecords from '@/model/UserTableRecords';
 import { getAppBaseUrl } from '@/utils/index';
 import { sendToken } from "@/hooks/useIframeToken";
 import { useSystemStore } from "@/store/modules/system";
 const systemStore = useSystemStore();
 *** PUBLISH_EXEGESIS ***/

    // 获取传入的流程ID和组件参数
    const props = defineProps({
        'processInstanceId': {
            type: [String, Number, Boolean],
            default: undefined
        },
        'type': {
            type: [String],
            default: undefined
        },
        'iterationVersionId': {
            type: [String, Number],
            default: undefined
        },
        'storyId': {
            type: [String, Number],
            default: undefined
        }
    })
    const immediateWatch = (source, callback) => {
        watch(source, callback, { deep: true, immediate: true })
    }

    /* -------------------------- 属性定义 -------------------------- */

    const setTimeOutList = []
    const pageLoading = reactive({
        loading: false,
        tip: undefined
    })
    const authInfo = localGet('app-user-store')

    // 处理校验
    const setSubFormName = (parentInfo, name) => {
        return [parentInfo.parents, parentInfo.cycleIndex, name]
    }

    /* ========================== 属性定义 ========================== */

    /* -------------------------- 元素数据引用 -------------------------- */

    /* ========================== 元素数据引用 ========================== */

    /* -------------------------- 页面数据 -------------------------- */

    /* ========================== 页面数据 ========================== */

    /* -------------------------- 响应 -------------------------- */

    /* ========================== 响应 ========================== */

    /* -------------------------- 函数 -------------------------- */

    /* ========================== 函数 ========================== */

    /* -------------------------- 页面参数 -------------------------- */

    const pageParameter_state = reactive({
        'processInstanceId': props.processInstanceId ?? route.query.processInstanceId
    })

    /* ========================== 页面参数 ========================== */

    /* -------------------------- 组件事件处理方法 -------------------------- */

    // ---------------------------- 组件数据同步 ----------------------------

    // ============================ 组件数据同步 ============================

    /* ========================== 组件事件处理方法 ========================== */

    /* -------------------------- 生命周期 -------------------------- */

    // 用于参数变更后重新执行自定义指令
    const queryUpdateFlag = ref(0)
    onMounted(() => {})

    onUnmounted(() => {
        // 销毁定时器
        setTimeOutList.forEach(({ repeat, timer }) => {
            repeat ? clearInterval(timer) : clearTimeout(timer)
        })
    })

    /* ========================== 生命周期 ========================== */

    /* -------------------------- 数据关联处理 -------------------------- */

    /* -------------------------- 表单数据同步 -------------------------- */
    /* ========================== 表单数据同步 ========================== */

    /* ========================== 数据关联处理 ========================== */

    /* -------------------------- 不同组件自定义的处理逻辑 -------------------------- */

    /* ========================== 不同组件自定义的处理逻辑 ========================== */

    // ================================定制化逻辑========================================
    // 判断 URL 是否带有 jump 参数
    const headerOffset = ref(20)
    const calcTableHeight = () => {
        state.tableHeight = window.innerHeight - 180 - headerOffset.value
    }

    onMounted(() => {
        calcTableHeight()
        window.addEventListener('resize', calcTableHeight)

        document.addEventListener('click', e => {
            const target = e.target as HTMLElement
            if (target.tagName === 'IMG' && target.classList.contains('previewable-image')) {
                const imgUrl = target.getAttribute('src')
                if (imgUrl) {
                    // 预览逻辑，如弹窗显示 imgUrl
                    previewImage(imgUrl)
                }
            }
        })
    })

    onBeforeUnmount(() => {
        window.removeEventListener('resize', calcTableHeight)
    })
    function previewImage(url) {
        const imgWindow = window.open()
        if (imgWindow) {
            imgWindow.document.write(`<img src="${url}" style="max-width:100%"/>`)
        }
    }
    const columns = ref([
        {
            title: '用户故事名称',
            dataIndex: 'storyName',
            resizable: true,
            align: 'center',
            ellipsis: true,
            width: 440,
            fixed: 'left',
            visible: true
        },
        {
            title: '所属子目标',
            dataIndex: 'projectMonthlyGoal',
            resizable: true,
            align: 'center',
            ellipsis: true,
            width: 200,
            visible: computed(() => showState.storyCheckList.includes('所属子目标'))
        },
        {
            title: '迭代项目名称',
            dataIndex: 'projectName',
            align: 'center',
            ellipsis: true,
            width: 120,

        },
        {
            title: '用户故事状态',
            dataIndex: 'storyStatus',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.storyCheckList.includes('用户故事状态'))
        },
        {
            title: '进度',
            dataIndex: 'progress',
            align: 'center',
            ellipsis: true,
            width: 220,
            visible: computed(() => showState.storyCheckList.includes('进度'))
        },
        {
            title: '优先级',
            dataIndex: 'priority',
            align: 'center',
            ellipsis: true,
            width: 80,
            visible: computed(() => showState.storyCheckList.includes('优先级'))
        },
        {
            title: '负责人',
            dataIndex: 'owner',
            align: 'center',
            ellipsis: true,
            width: 80,
            visible: computed(() => showState.storyCheckList.includes('负责人'))
        },

        {
            title: '计划开始',
            dataIndex: 'startTime',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.storyCheckList.includes('计划开始'))
        },
        {
            title: '实际开始',
            dataIndex: 'actualStartTime',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.storyCheckList.includes('实际开始'))
        },
        {
            title: '计划完成',
            dataIndex: 'endTime',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.storyCheckList.includes('计划完成'))
        },
        {
            title: '实际完成',
            dataIndex: 'actualEndTime',
            align: 'center',
            ellipsis: true,
            width: 200,
            visible: computed(() => showState.storyCheckList.includes('实际完成'))
        },
        {
            title: '最新备注',
            dataIndex: 'actualEndTime',
            align: 'center',
            ellipsis: true,
            width: 200,
            visible: computed(() => showState.storyCheckList.includes('最新备注'))
        },
        {
            title: '创建人',
            dataIndex: 'createdBy',
            align: 'center',
            ellipsis: true,
            width: 80,
            visible: computed(() => showState.storyCheckList.includes('创建人'))
        },
        {
            title: '创建时间',
            dataIndex: 'createdTime',
            align: 'center',
            ellipsis: true,
            width: 200,
            visible: computed(() => showState.storyCheckList.includes('创建时间'))
        },
        {
            title: '操作',
            dataIndex: 'operation',
            align: 'center',
            fixed: 'right',
            width: 100,
            visible: computed(() => showState.storyCheckList.includes('创建时间'))
        }
    ])
    const innerColumns = ref([
        {
            title: '任务名称',
            dataIndex: 'taskName',
            align: 'center',
            ellipsis: true,
            resizable: true,
            width: 320,
            fixed: 'left',
            visible: true
        },
        {
            title: '任务状态',
            dataIndex: 'taskStatus',
            align: 'center',
            resizable: true,
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.taskCheckList.includes('任务状态'))
        },
        {
            title: '优先级',
            dataIndex: 'priority',
            resizable: true,
            align: 'center',
            ellipsis: true,
            width: 80,
            visible: computed(() => showState.taskCheckList.includes('优先级'))
        },
        {
            title: '审核标准',
            dataIndex: 'assessorStandard',
            resizable: true,
            align: 'center',
            ellipsis: true,
            width: 80,
            visible: computed(() => showState.taskCheckList.includes('审核标准'))
        },
        {
            title: '任务负责人',
            dataIndex: 'taskOwner',
            resizable: true,
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.taskCheckList.includes('任务负责人'))
        },
        {
            title: '任务执行人',
            dataIndex: 'taskParticipant',
            resizable: true,
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.taskCheckList.includes('任务执行人'))
        },
        {
            title: '审核员',
            dataIndex: 'assessorName',
            resizable: true,
            align: 'center',
            ellipsis: true,
            width: 80,
            visible: computed(() => showState.taskCheckList.includes('审核员'))
        },
        {
            title: '计划开始',
            dataIndex: 'startTime',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.taskCheckList.includes('计划开始'))
        },
        {
            title: '实际开始',
            dataIndex: 'examPassTime',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.taskCheckList.includes('实际开始'))
        },
        {
            title: '计划完成',
            dataIndex: 'endTime',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.taskCheckList.includes('计划完成'))
        },
        {
            title: '实际完成',
            dataIndex: 'taskFinishTime',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.taskCheckList.includes('实际完成'))
        },
        {
            title: '计划工时',
            dataIndex: 'workingHours',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.taskCheckList.includes('计划工时'))
        },
        {
            title: '实际工时',
            dataIndex: 'actualWorkHours',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.taskCheckList.includes('实际工时'))
        },
        {
            title: '附件模板',
            dataIndex: 'attachmentTemplateList',
            align: 'center',
            ellipsis: true,
            width: 120,
            visible: computed(() => showState.taskCheckList.includes('附件模板'))
        },
        {
            title: '附件',
            dataIndex: 'attachmentList',
            align: 'center',
            ellipsis: true,
            width: 80,
            visible: computed(() => showState.taskCheckList.includes('附件'))
        },
        {
            title: '最新备注',
            dataIndex: 'actualEndTime',
            align: 'center',
            ellipsis: true,
            width: 200,
            visible: computed(() => showState.storyCheckList.includes('最新备注'))
        },
        {
            title: '创建人',
            dataIndex: 'createBy',
            align: 'center',
            ellipsis: true,
            width: 80,
            visible: computed(() => showState.taskCheckList.includes('创建人'))
        },
        {
            title: '创建时间',
            dataIndex: 'createTime',
            align: 'center',
            ellipsis: true,
            width: 200,
            visible: computed(() => showState.taskCheckList.includes('创建时间'))
        }
    ])
    // 获取所有数据
    function getData() {
        state.tableLoading = true
        post('/solution-ppm/api/v2/built-in-system-ppm/v3/platform_task/project_story_task_list', {
            storyId: props.storyId,
            iterationVersionId: props.iterationVersionId
        })
            .then(data => {
                if (data.code !== 200) {
                    message.error(data.message)
                    state.dataSource = []
                    return
                }
                state.dataSource = data.data
                const storyKeys = []
                const taskKeys = {}

                state.dataSource.forEach(story => {
                    if (story.keynote === '是') {
                        storyKeys.push(story.id)
                    }
                    if (story.taskChildrenList?.length) {
                        const selectedChildren = story.taskChildrenList
                            .filter(t => t.keynote === '是')
                            .map(t => t.taskId)
                        if (selectedChildren.length > 0) {
                            taskKeys[story.id] = selectedChildren
                        }
                    }
                })

                state.selectedStoryKeys = storyKeys
                state.selectedTaskKeys = taskKeys
            })
            .finally(() => {
                state.tableLoading = false
            })
    }
    const userInfoStorage = localStorage.getItem('al__USER_INFO')
    const taskLeader = JSON.parse(userInfoStorage)
    // 解析 window.location.search
    function parseSearchParams() {
        const search = window.location.search.substring(1)
        const params = new URLSearchParams(search)
        const result: Record<string, string> = {}
        params.forEach((value, key) => {
            result[key] = value
        })
        return result
    }

    // 统一入口参数，优先级：props > route.query > window.location.search
    const effectiveParams = computed(() => {
        return {
            ...parseSearchParams(),
            ...route.query,
            ...props
        }
    })
    // 页面整体数据控制
    const state = reactive({
        dataSource: [],
        expandedRowKeys: []
    })
    const showState = reactive({
        storyCheckList: [],
        taskCheckList: [],
        storyAllCheck: false,
        allStoryOptions: getStoryOptions(),
        allTaskOptions: getTaskOptions(),
        taskAllCheck: false,
        settingVisible: false,
        settingLoading: false
    })
    function getStoryOptions() {
        return columns.value.filter(c => !['操作', '用户故事名称'].includes(c.title)).map(c => c.title)
    }

    function getTaskOptions() {
        return innerColumns.value.filter(c => !['操作', '任务名称'].includes(c.title)).map(c => c.title)
    }

    const storyRowSelection = computed(() => {
        return state.showCheck
            ? {
                  selectedRowKeys: state.selectedStoryKeys,
                  onChange: keys => {
                      state.selectedStoryKeys = keys
                  }
              }
            : null
    })
    // 页面整体数据控制
    // 初始化/监听变化
    const useProjectId = ref()
    watch(
        () => [props.storyId, props.iterationVersionId],
        ([params]) => {
            console.log('生效的参数：', params)
            getData()
        },
        { immediate: true }
    )

    function initPage(params) {
        useProjectId.value = params.projectId || params.id
        getData()
    }

    const finishPercent = item => {
        const total = item.taskNumber || 1
        return ((item.finishTaskNumber / total) * 100).toFixed(2)
    }

    const totalPercent = item => {
        const total = item.taskNumber || 1
        return (((item.finishTaskNumber + item.runningTaskNumber) / total) * 100).toFixed(2)
    }
    //     用户故事部分=====================================================================================================================================================================================
    //     用户故事部分=====================================================================================================================================================================================
    //     用户故事部分=====================================================================================================================================================================================
    function handleResizeStoryColumn(w, col) {
        col.width = w
    }
    function getBgc(type) {
        if (type === 'P0') {
            return '#FF4D4F'
        } else if (type === 'P1') {
            return '#FF7A45'
        } else if (type === 'P2') {
            return '#FFA940'
        } else if (type === 'P3') {
            return '#FFC53D'
        }
    }
    const typeColorMap = {
        'SOP任务': '#FF85C0',
        '子任务': '#FF85C0',
        '问题': '#FF85C0'
    }
    function getStatusColor(status) {
        return colorMap[status] || '#9DBAFF'
    }
    function getTypeColor(status) {
        return typeColorMap[status] || '#3875ff'
    }
    const classMap = {
        '问题': 'question-task-bg',
        '子任务': 'son-task-bg',
        'SOP任务': 'sop-task-bg'
    }
    const colorMap = {
        '进行中': '#3875FF',
        '审批中': '#1890FF',
        '未启动': '#BBBBBB',
        '完成': '#52C41A',
        '延期完成': '#FA8C16',
        '测试中': '#A0D911',
        '延期': '#FA3D16',
        '提测': '#A0D911',
        '验收': '#9254DE'
    }
    // 格式化函数
    function formatPercentage(value) {
        if (value === 0) return '0'
        return value.toFixed(2)
    }
    //     用户故事部分=====================================================================================================================================================================================
    //     用户故事部分=====================================================================================================================================================================================
    //     用户故事部分=====================================================================================================================================================================================
    //     任务部分=====================================================================================================================================================================================
    //     任务部分=====================================================================================================================================================================================
    //     任务部分=====================================================================================================================================================================================
    function handleResizeTaskColumn(w, col) {
        col.width = w
    }

    // 判断是否已全部展开
    const isAllExpanded = computed(() => state.expandedRowKeys.length === state.dataSource.length)
    const expendSping = ref(false)
    // 切换展开/收起全部
    const toggleExpandAll = () => {
        expendSping.value = true
        if (isAllExpanded.value) {
            state.expandedRowKeys = [] // 收起
        } else {
            state.expandedRowKeys = state.dataSource.map(item => item.id) // 全部展开
        }
        expendSping.value = false
    }
    // 跳转到任务
    function jumpTask(type, versionId, projectId, storyId) {
        window.open(
            `${
                import.meta.env.VITE_PUBLIC_PATH
            }/page/3000001150?id=${projectId}&iterationVersionId&storyId=${storyId}&jump=true`,
            '_blank'
        )
    }

    // ---------- 格式化函数（防空、稳定返回类型） ----------
    const normalizeToArray = v => {
        if (v == null) return []
        if (Array.isArray(v)) return v.map(i => (i == null ? '' : String(i).trim()))
        return String(v)
            .split(/[,;；]/)
            .map(i => i.trim())
    }

    const cleanArray = arr => arr.filter(i => i && i !== 'null' && i !== 'undefined')

    const formatAllCodes = ids => {
        const arr = cleanArray(normalizeToArray(ids))
        if (!arr.length) return ''
        arr.sort()
        return arr.join('；')
    }

    const formatFirstCode = ids => {
        const arr = cleanArray(normalizeToArray(ids))
        if (!arr.length) return ''
        arr.sort()
        return arr.length === 1 ? arr[0] : `${arr[0]}...`
    }

    const formatAllLabels = labels => {
        const arr = cleanArray(normalizeToArray(labels))
        if (!arr.length) return ''
        arr.sort()
        return arr.join('；')
    }

    // 返回用于渲染的数组（**始终返回数组**）
    const formatShowLabels = labels => {
        const arr = cleanArray(normalizeToArray(labels))
        if (!arr.length) return []
        arr.sort()
        if (arr.length > 2) return [arr[0], `${arr[1]}...`]
        return arr
    }

    // ---------- 更严格的存在判断（避免空字符串导致的 children 为空） ----------
    const hasLabels = record => {
        try {
            return formatShowLabels(record?.labels).length > 0
        } catch (e) {
            return false
        }
    }
    const hasNumberList = record => {
        try {
            return cleanArray(normalizeToArray(record?.numberList)).length > 0
        } catch (e) {
            return false
        }
    }
</script>
<style lang="less" scoped>
    .page_root {
        width: 100%;
        height: 100%;
        overflow: auto;
    }
    .page_root :deep(.@{ant-prefix}-spin-nested-loading),
    .page_root :deep(.@{ant-prefix}-spin-container) {
        height: 100%;
    }
    .page_root :deep(.@{ant-prefix}-spin-container .@{ant-prefix}-table-wrapper) {
        display: flex;
        flex-flow: column nowrap;
        justify-content: flex-start;
    }
    .page_root :deep(.@{ant-prefix}-table) {
        flex: auto;
        overflow: auto;
    }
    .table-moreBtn_down {
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        width: 100px;
    }

    .table-moreBtn_menu :deep(.table-moreBtn_menu-item):hover {
        background: #ebf1ff;
    }

    /*  表格树结构样式处理，展开按钮层级增加*/
    :deep(.@{ant-prefix}-table-row-expand-icon) {
        z-index: 1;
    }
    /*  表格树结构 由于每个表格项采用自定义组件，使用绝对定位布局，开启树结构导致显示异常，暂时使用层级定位位置 */
    :deep(.@{ant-prefix}-table-row-level-1 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
        left: 15px !important;
        width: calc(100% - 15px);
    }
    :deep(.@{ant-prefix}-table-row-level-2 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
        left: 30px !important;
        width: calc(100% - 30px);
    }
    :deep(.@{ant-prefix}-table-row-level-3 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
        left: 45px !important;
        width: calc(100% - 45px);
    }
    :deep(.@{ant-prefix}-table-row-level-4 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
        left: 60px !important;
        width: calc(100% - 60px);
    }

    /* 穿梭框改变默认样式，为根据组件样式控制左右栏宽度。 */
    :deep(.@{ant-prefix}-transfer-list) {
        height: 100%;
        width: 50%;
    }
    /*样式*/
    .body {
        height: 100%;
        background-color: #fff;
        overflow: hidden;
        padding: 24px;
    }
    .top-search :deep(.ant3-form-item-label) {
        width: 100px;
        white-space: break-spaces;
        text-align: center;
    }
    .story-name-select:deep(.ant3-form-item-control) {
        overflow: hidden;
    }
    .story-name-select:deep(.ant3-form-item) {
        display: flex !important;
        flex-wrap: nowrap !important;
    }
    .story-name-select:deep(.ant3-form-item-label) {
        width: 100px !important;
        flex-shrink: 0;
    }
    .top-search :deep(.ant3-form-item-label > label) {
        height: 35px !important;
    }
    .top-search {
        background-color: #fff;
    }
    .top-search :deep(.ant3-row) {
        height: 40px;
    }
    .top-search :deep(.ant3-col) {
        height: 40px;
    }
    .mt-4 {
        margin: 0;
    }
    .content {
        background-color: #fff;
        padding: 8px 16px 16px 16px;
        height: 100%;
    }
    .table-top {
        display: flex;
        justify-content: space-between;
        margin-bottom: 16px;
        margin-top: 12px;
        align-items: center;
    }
    .tip-msg {
        padding-left: 8px;
        border-left: 4px solid #3875ff;
        height: 16px;
        font-size: 16px;
        font-weight: 600;
        line-height: 16px;
    }
    .right-btn {
        display: flex;
        gap: 8px;
    }

    .form-model :deep(.ant3-form-item-label) {
        width: 110px;
    }
    :deep(.ql-toolbar) {
        background: #fafafa;
        border: 1px solid #d9d9d9;
        border-radius: 2px 2px 0 0;
    }
    :deep(.ql-blank) {
        min-height: 110px;
        background: #ffffff;
    }
    :deep(.ql-editor) {
        min-height: 110px;
        background: #ffffff;
    }
    .table-box-task:deep(
            .ant3-table.ant3-table-bordered > .ant3-table-container > .ant3-table-content > table > tbody > tr > td
        ) {
        padding: 8px 16px;
    }
    .table-box-story:deep(
            .ant3-table.ant3-table-bordered.ant3-table-small
                > .ant3-table-container
                > .ant3-table-body
                > table
                > tbody
                > tr
                > td
                > .ant3-table-expanded-row-fixed
        ) {
        padding-left: 22px !important;
        padding-bottom: 5px !important;
    }
    .table-box-story:deep(.ant3-table-expanded-row-fixed) {
        padding: 0;
    }
    .mbtask-form :deep(.ant3-form-item-label) {
        width: 115px !important;
    }
    .type-tag {
        flex-shrink: 0;
        text-align: left;
        border-radius: 4px;
        padding: 0 4px;
    }
    .task-title {
        display: flex;
        gap: 4px;
        background: linear-gradient(to bottom, rgba(255, 255, 255, 0.92), #fff, #fff);
        align-items: center;
    }
    .name-color,
    .story-name-color {
        color: #262626;
    }
    .story-name-color {
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }
    .priority-tag {
        font-style: oblique;
        background: #ccc;
        border-radius: 0;
        font-size: 14px;
        color: #ffffff;
        letter-spacing: 0;
        line-height: 22px;
        font-weight: 500;
        height: 22px;
        text-align: center;
        padding: 0 8px; /* 可根据内容调整内边距 */

        /* 自动根据内容撑开宽度 */
        display: inline-flex;
        justify-content: center;
        align-items: center;
    }

    .status-tag {
        font-size: 14px;
        color: #ffffff;
        letter-spacing: 0;
        text-align: center;
        line-height: 22px;
        font-weight: 500;
        width: 55px;
        height: 22px;
        border-radius: 2px;
        flex: 1;
    }
    .edit-approve {
        box-shadow: rgb(193, 205, 223) 0px 2px 6px;
        text-align: right;
        display: flex;
    }
    .can-edit {
        width: 16px;
        height: 16px;
        background-image: url('http://10.40.0.33:9001/file-storage/523231194136260608-source.png');
        background-size: cover;
        opacity: 0.7;
        cursor: pointer;
    }
    .question-form :deep(.ant3-form-item-label) {
        width: 115px !important;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        display: inline-block;
    }
    .critical-path {
        height: 20px;
        width: 20px;
        background-image: url('http://10.40.0.33:9001/file-storage/528348581051316224-source.png');
        background-size: cover;
    }
    .story-status {
        margin: 0 auto;
    }
    .story-bg {
        height: 24px;
        width: 24px;
        background-image: url('http://10.40.0.33:9001/file-storage/527026039625691136-source.png');
        background-size: cover;
    }
    .sop-task-bg {
        height: 24px;
        width: 24px;
        background-image: url('http://10.40.0.33:9001/file-storage/527026395994730496-source.png');
        background-size: cover;
    }
    .son-task-bg {
        height: 24px;
        width: 24px;
        background-image: url('http://10.40.0.33:9001/file-storage/527026489932460032-source.png');
        background-size: cover;
    }
    .question-task-bg {
        height: 24px;
        width: 24px;
        background-image: url('http://10.40.0.33:9001/file-storage/527026529524592640-source.png');
        background-size: cover;
    }
    /*样式*/

    .title-new-status {
        height: 62px;
        line-height: 36px;
        padding: 0 16px;
        margin-bottom: 16px;
        background-image: url('http://10.40.0.33:9001/file-storage/447701084442329088-source.png');
        background-size: cover;
        border: 1px solid #3875ff;
        border-radius: 5px;
    }

    :deep(.ant3-steps-item-container) {
        padding: 0 16px;
    }
    :deep(.ant3-steps-item-tail::after) {
        margin-left: 12px;
        background-color: #3875ff;
    }
    :deep(.ant3-steps-item-description) {
        font-size: 12px;
        color: #595959;
        text-align: left;
        line-height: 20px;
        font-weight: 400;
    }
    :deep(.ant3-steps-item-icon) {
        width: 24px;
        height: 24px;
        line-height: 24px;
        color: #3875ff;
        margin-top: 6px;
    }
    :deep(.ant3-steps-item-title) {
        height: 28px;
        font-size: 14px;
        color: #262626;
        text-align: left;
        line-height: 22px;
        font-weight: 400;
    }
    :deep(.ant3-steps-item-process > .ant3-steps-item-container > .ant3-steps-item-icon) {
        background-color: #3875ff;
    }
    :deep(.ant3-steps-vertical > .ant3-steps-item > .ant3-steps-item-container > .ant3-steps-item-tail::after) {
        background: #3875ff;
    }
    :deep(.status-step .ant3-steps-vertical > .ant3-steps-item .ant3-steps-item-icon) {
        border-color: #3875ff;
    }
    :deep(.ant3-steps-item-finish .ant3-steps-item-icon > .ant3-steps-icon) {
        color: #3875ff !important;
    }
    .task-status-new {
        cursor: pointer;
    }
    .left-title,
    .right-title {
        font-size: 14px;
        color: rgba(0, 0, 0, 0.65);
        letter-spacing: 0;
        text-align: left;
        font-weight: 400;
        height: 31px;
        line-height: 31px;
    }
    .weight-status {
        font-size: 14px;
        color: #262626;
        letter-spacing: 0;
        font-weight: 500;
    }
    .batch-line {
        width: 100%;
        border-top: 1px solid #d9d9d9;
        margin: 16px 0;
    }
    .weight-status-new {
        font-weight: 600;
        color: #262626;
    }
    .task-collaborate {
        width: 58px;
        text-align: center;
        height: 16px;
        line-height: 16px;
        position: absolute;
        top: 12px;
        left: 9px;
        background-color: #eee5fa;
        color: #9254de;
    }
    .task-name-title-right {
        margin-left: 80px;
    }
    .name-color:hover,
    .story-name-color:hover {
        //cursor: pointer;
        //color: #3875ff;
    }
    .description-title {
        font-weight: 600;
        margin-bottom: 8px;
        border-left: 4px solid #3875ff;
        padding-left: 6px;
        height: 14px;
        line-height: 14px;
    }
    /* 外层允许小屏水平滚动 */
    .desc-wrapper {
        width: 100%;
        overflow-x: auto;
    }
    .desc-table {
        margin-bottom: 8px;
    }
    /* 表格样式：实线、单元格对齐、固定 label 宽度 */
    .desc-table {
        width: 100%;
        border-collapse: collapse;
        table-layout: fixed; /* 让列按 colgroup 分配宽度 */
        font-size: 13px;
    }

    .desc-table th,
    .desc-table td {
        border: 1px solid #e8e8e8;
        padding: 10px 12px;
        vertical-align: middle;
        background: #fff;
        box-sizing: border-box;
    }

    /* label 样式（左侧） */
    .desc-table th {
        background: #f2f6fc;
        font-weight: 600;
        text-align: left;
        white-space: nowrap; /* label 不换行 */
        writing-mode: horizontal-tb !important; /* 防止被全局样式转成竖排 */
        transform: none !important;
    }

    /* value 栏：支持换行但太长时不撑破布局（单元格内部换行） */
    .desc-table td {
        white-space: normal;
        word-break: break-word;
        overflow: hidden;
    }

    /* 当你希望某些长文本一行显示并省略（可按需打开） */

    .desc-table td.single-line {
        white-space: nowrap;
        text-overflow: ellipsis;
    }
    .description-body {
        margin-bottom: 8px;
    }
    .story-header {
        position: relative;
    }
    .expend-btn {
        position: absolute;
        top: 10px;
        left: 10px; /* 往左偏移 */
        z-index: 20; /* 高于表格单元格 */
        cursor: pointer;
    }
    .table-box-story {
        position: relative;
    }
    .history-remark-title {
        display: flex;
        justify-content: end;
        margin-bottom: 16px;
    }
    .transfer-tip {
        font-size: 12px;
        letter-spacing: 0;
        text-align: justify;
        font-weight: 400;
        color: #ff8022;
        background: rgba(250, 100, 0, 0.05);
        padding: 6px 16px;
        margin-right: 8px;
    }
</style>
